﻿using System;
using Framework.Core.Cache;
using Framework.Core.Reflection;
using Rhino.Mocks;
using Xunit;

namespace Framework.Tests.Core.Cache
{
    extern alias castlecore;

    public class CacheInterceptorTest
    {
        interface IPouet
        {
            void JustDoIt();
        }
        
        private readonly ICacheKeyBuilder cacheKeyBuilderMock;
        private readonly IMethodFactory methodFactoryMock;
        private readonly castlecore::Castle.DynamicProxy.IInvocation invocationMock;
        private readonly MockRepository mockRepository;
        private readonly IStorage storageMock;
        private readonly CacheInterceptor target;

        public CacheInterceptorTest()
        {
            mockRepository = new MockRepository();
            cacheKeyBuilderMock = mockRepository.StrictMock<ICacheKeyBuilder>();
            storageMock = mockRepository.StrictMock<IStorage>();
            methodFactoryMock = mockRepository.StrictMock<IMethodFactory>();
            invocationMock = mockRepository.StrictMock<castlecore::Castle.DynamicProxy.IInvocation>();
            target = new CacheInterceptor(cacheKeyBuilderMock, storageMock, methodFactoryMock);
        }

        [Fact]
        public void Should_get_if_exists_in_cache()
        {
            var expected = MockCache("pouet", true);

            mockRepository.ReplayAll();
            target.Intercept(invocationMock);
            mockRepository.VerifyAll();

            Assert.Equal(expected, invocationMock.ReturnValue);
        }

        private object MockCache(string cacheKey, bool isCached)
        {
            invocationMock.Expect(x => x.ReturnValue).PropertyBehavior();

            var methodMock = new Method("ass", "t", "n", new IArgument[] { });
            methodFactoryMock.Expect(x => x.Create(invocationMock))
                             .Return(methodMock);
            cacheKeyBuilderMock.Expect(x => x.Build(methodMock)).Return(cacheKey);
            object cache;
            var expected = new object();
            var expectStorage = storageMock.Expect(x => x.TryGet(cacheKey, out cache));

            if (isCached)
            {
                expectStorage.OutRef(new[] { expected }).Return(true);
            }
            else
            {
                expectStorage.Return(false);
            }

            return expected;
        }

        [Fact]
        public void Should_update_if_not_exists_in_cache()
        {
            MockCache("pouet", false);
            var expected = new object();
            invocationMock.Expect(x => x.Proceed()).WhenCalled(x => invocationMock.ReturnValue = expected);
            storageMock.Expect(x => x.Update("pouet", new TimeSpan(), expected)).IgnoreArguments();

            mockRepository.ReplayAll();
            target.Intercept(invocationMock);
            mockRepository.VerifyAll();

            Assert.Equal(expected, invocationMock.ReturnValue);
        }

        [Fact]
        public void Should_flush_cache()
        {
            storageMock.Expect(x => x.Flush());
            mockRepository.ReplayAll();
            target.Flush();
            mockRepository.VerifyAll();
        }
    }
}